#include "visitor.hpp"
#include "ast.hpp"
#include "type.hpp"

#include <iostream>
using namespace std;

Node* Visitor::visit(Node* n) {
    return n->accept(this);
}

void Dumper::print_indent() {
    for (int i = 0; i < _indent; i++) {
        printf("  ");
    }
}

Node* Dumper::visit(Node* n) {
    return n->accept(this);
}

Node* Dumper::visit(PrintNode* n) {
    cout << "print(";
    visit(n->value());
    cout << ")";
    return n;
}

Node* Dumper::visit(ReturnNode* n) {
    cout << "return ";
    visit(n->value());
    return n;
}

Node* Dumper::visit(BinaryOp* op) {
    switch(op->op_type()) {
    case AST_OP_ADD:
        printf("(+ ");
        break;
    case AST_OP_SUB:
        printf("(- ");
        break;
    case AST_OP_MUL:
        printf("(* ");
        break;
    case AST_OP_DIV:
        printf("(/ ");
        break;
    case AST_OP_MOD:
        printf("(%% ");
        break;
    case AST_OP_BIT_AND:
	    printf("(& ");
        break;
    case AST_OP_BIT_OR:
	    printf("(| ");
        break;
    case AST_OP_BIT_XOR:
	    printf("(^ ");
        break;
    case AST_OP_LOG_OR:
	    printf("(or ");
        break;
    case AST_OP_LOG_AND:
	    printf("(and ");
        break;
    case AST_OP_LEFT_SHIFT:
	    printf("(<< ");
        break;
    case AST_OP_RIGHT_SHIFT:
	    printf("(>> ");
        break;
    case AST_OP_COMPOSE:
        printf("(|> ");
        break;
    default:
        printf("Unknown binary op %d\n", op->op_type());
        return op;
    }

    visit(op->left());
    printf(" ");
    visit(op->right());
    printf(")");
    return op;
}

Node* Dumper::visit(LogicNotNode* n) {
    cout << "(not ";
    visit(n->value());
    cout << ")";
    return n;
}

Node* Dumper::visit(ListNode* n) {
    print_indent();
    printf("[\n");
    _indent++;
    for (auto it = n->node_list()->begin(); 
        it != n->node_list()->end(); it++) {
        print_indent();
        visit(*it);
        printf(", \n");
    }
    _indent--;
    print_indent();
    printf("]");
    return n;
}

Node* Dumper::visit(StmtsNode* n) {
    print_indent();
    printf("[\n");
    _indent++;
    for (auto it = n->node_list()->begin(); 
        it != n->node_list()->end(); it++) {
        print_indent();
        visit(*it);
        printf(", \n");
    }
    _indent--;
    print_indent();
    printf("]");
    return n;
}

Node* Dumper::visit(ConstInt* n) {
    cout << n->value();
    return n;
}

Node* Dumper::visit(ConstDouble* n) {
    cout << n->value();
    return n;
}

Node* Dumper::visit(ConstString* n) {
    printf("%s", n->_value);
    return n;
}

Node* Dumper::visit(ConstArray* n) {
    cout << "[";
    for (auto it = n->arr()->begin(); it != n->arr()->end(); it++) {
        visit(*it);
        cout << ", ";
    }
    cout << "]";
    return n;
}

Node* Dumper::visit(ImportNode* n) {
    if (n->packages().size() > 0) {
        cout << "from ";
        for (int i = 0; i < n->packages().size() - 1; i++) {
            cout << n->packages()[i] << ".";
        }
        cout << n->packages()[n->packages().size() - 1] << " ";
    }

    cout << "import " << n->name();
    if (n ->alias() != "") {
        cout << " as " << n->alias();
    }
    return n;
}

Node* Dumper::visit(ImplementNode* n) {
    cout << "implement " << n->trait_name() << " for " << n->for_name() << " {";
    visit(n->methods());
    cout << "}";
    return n;
}

Node* Dumper::visit(IfNode* n) {
    printf("if(");
    visit(n->cond());
    printf(") then\n");
    visit(n->then_part());

    if (n->else_part() != NULL) {
        printf(" else\n");
        visit(n->else_part());
    }
    return n;
}

Node* Dumper::visit(WhileNode* n) {
    printf("while (");
    visit(n->cond());
    printf(")\n");
    visit(n->body());
    return n;
}

Node* Dumper::visit(CmpNode* n) {
    switch(n->cmp_op()) {
    case CmpNode::CMP_LT:
        cout << "(< ";
        break;
    case CmpNode::CMP_LE:
        cout << "(<= ";
        break;
    case CmpNode::CMP_GT:
        cout << "(> ";
        break;
    case CmpNode::CMP_GE:
        cout << "(>= ";
        break;
    case CmpNode::CMP_EQU:
        cout << "(== ";
        break;
    }

    visit(n->left());
    printf(" ");
    visit(n->right());
    printf(")");
    return n;
}

Node* Dumper::visit(AssignNode* n) {
    printf("(= ");
    visit(n->left());
    printf(" ");
    visit(n->right());
    printf(")");
    return n;
}

Node* Dumper::visit(VarNode* n) {
    cout << n->name();
    if (n->_type) {
        cout << " : " << n->_type->to_string();
    }

    return n;
}

Node* Dumper::visit(VarDefNode* n) {
    cout << n->name();

    if (n->_type) {
        cout << " : " << n->_type->to_string();
    }
    else if (n->def_type()) {
        cout << " : ";
        visit(n->def_type());
    }

    if (n->init_value()) {
        printf(" = ");
        visit(n->init_value());
    }
    return n;
}

Node* Dumper::visit(ParamDefNode* n) {
    cout << n->name();
    if (n->def_type()) {
        cout << " : ";
        visit((Node*)n->def_type());
    }
    if (n->default_value()) {
        cout << " = ";
        visit(n->default_value());
    }
    return n;
}

Node* Dumper::visit(FuncDefNode* n) {
    if (n->ret_type_node()) {
        visit(n->ret_type_node());
    }
    else {
        cout << "Bottom";
    }

    cout << " " << n->name() << "(";
    visit(n->args());
    cout << ")";
    if (n->body()) {
        visit(n->body());
    }
    return n;
}

Node* Dumper::visit(RecordDefNode* n) {
    cout << "record " << n->name() << " {";
    visit(n->fields());
    cout << "}";
    return n;
}

Node* Dumper::visit(TraitDefNode* n) {
    cout << "trait " << n->name() << " {";
    visit(n->methods());
    cout << "}";
    return n;
}

Node* Dumper::visit(LambdaDef* n) {
    cout << "lambda (";
    visit(n->param());
    cout << ") => ";
    visit(n->body());
    return n;
}

Node* Dumper::visit(CallNode* n) {
    visit(n->callee());
    cout << "(";
    visit(n->args());
    cout << ")";
    return n;
}

Node* Dumper::visit(SubscrNode* n) {
    visit(n->name());
    cout << "[";
    visit(n->index());
    cout << "]";
    return n;
}

Node* Dumper::visit(DotNode* n) {
    visit(n->name());
    cout << " . ";
    visit(n->field());
    return n;
}

Node* Dumper::visit(SequenceNode* n) {
    visit(n->left());
    cout << " .. ";
    visit(n->right());
    return n;
}

Node* Dumper::visit(BuiltinConst* n) {
    switch (n->_builtin_id) {
    case BuiltinConst::UNIT:
        cout << "unit";
        break;
    case BuiltinConst::TRUE:
        cout << "true";
        break;
    case BuiltinConst::FALSE:
        cout << "false";
        break;
    default:
        cout << "unknown const: " << n->_builtin_id;
    }
    return n;
}

Node* Dumper::visit(TupleNode* n) {
    cout << "(";
    visit(n->args());
    cout << ")";
    return n;
}

Node* Dumper::visit(TypeTupleNode* n) {
    cout << "(";
    visit(n->args());
    cout << ")";
    return n;
}

Node* Dumper::visit(TypeVarDefNode* n) {
    cout << "typedef ";
    visit(n->value());
    cout << " " << n->name();
    return n;
}

Node* Dumper::visit(TypeFuncDefNode* n) {
    cout << "typedef poly <";
    visit(n->args());
    cout << "> ";
    visit(n->body());
    cout << " " << n->name();
    return n;
}

Node* Dumper::visit(TypeVarNode* n) {
    cout << n->name();
    return n;
}

Node* Dumper::visit(ArrowTypeNode* n) {
    visit((Node*)n->src());
    cout << " -> ";
    visit((Node*)n->dst());
    return n;
}

Node* Dumper::visit(ArrayTypeNode* n) {
    visit(n->elm_type());
    cout << "[" << n->size() << "]";
    return n;
}

Node* Dumper::visit(TypeCallNode* n) {
    visit(n->callee());
    cout << "<";
    visit(n->args());
    cout << ">";
    return n;
}

Node* Dumper::visit(TypeConstInt* n) {
    cout << n->value;
    return n;
}

